package com.gitee.wsl.transform.validator.sample

import com.gitee.wsl.base.plus
import com.gitee.wsl.transform.validator.string.StringValidator
import com.gitee.wsl.transform.validator.string.stringValidator
import kotlinx.datetime.LocalDate

private val provinces = arrayOf(
    null, null, null, null, null,
    null, null, null, null, null, null, "北京", "天津", "河北", "山西", "内蒙古",
    null, null, null, null, null, "辽宁", "吉林", "黑龙江", null, null, null,
    null, null, null, null, "上海", "江苏", "浙江", "安微", "福建", "江西", "山东",
    null, null, null, "河南", "湖北", "湖南", "广东", "广西", "海南", null, null,
    null, "重庆", "四川", "贵州", "云南", "西藏", null, null, null, null, null,
    null, "陕西", "甘肃", "青海", "宁夏", "新疆", null, null, null, null, null,
    "台湾", null, null, null, null, null, null, null, null, null, "香港",
    "澳门", null, null, null, null, null, null, null, null, "国外"
)

private val wi = intArrayOf(
    7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10,
    5, 8, 4, 2, 1
)
private val codes = charArrayOf(
    '1', '0', 'X', '9', '8', '7', '6',
    '5', '4', '3', '2'
)

/**
 * 根据GB11643-1999<<公民身份号码>>及GB11643-1989<<社会保障号码>>规定检查身份证号是否符合规范
</社会保障号码></公民身份号码> */
internal fun identify(ident: String): Triple<Boolean,String?,LocalDate?> {

    val len = ident.length
    if (len != 15 && len != 18) {
        return (false to null) + null
    }

    for (i in 0 until (if ((len == 15)) 15 else 17)) {
        val ch = ident[i]
        if (ch < '0' || ch > '9') {
            return (false to null) + null
        }
    }


    val addr = getAddr(ident)?:return (false to null) + null

    val date = getDate(ident)?:return (false to addr) + null

    // 检查校验码
    if (len == 18) {
        val w = wi
        var mod = 0
        for (i in 0..16) {
            mod += (ident[i].code - '0'.code) * w[i]
        }
        mod %= 11
        if (ident[17] != codes[mod]) {
            return (false to addr) + date
        }
    }
    return (true to addr) + date
}

// 检查户口所在县的行政区代码 GB/T2260
internal fun getAddr(ident: String):String?{
    val p = (ident[0].code - '0'.code) * 10 + (ident[1].code - '0'.code)
    if ( p < provinces.size ) {
        return provinces[p]
    }
    return null
}

// 检查出生年月日 GB/T7408
internal fun getDate(ident: String):LocalDate?{
    var year = 0
    var month = 0
    var day = 0
    val len = ident.length
    if (len == 15) {
        year = 1900 + (ident[6].code - '0'.code) * 10 + (ident[7].code - '0'.code)
        month = (ident[8].code - '0'.code) * 10 + (ident[9].code - '0'.code)
        day = (ident[10].code - '0'.code) * 10 + (ident[11].code - '0'.code)
    } else {
        year = (ident[6].code - '0'.code) * 1000 + ((ident[7].code - '0'.code)
                * 100) + ((ident[8].code - '0'.code) * 10
                ) + (ident[9].code - '0'.code)
        month = (ident[10].code - '0'.code) * 10 + (ident[11].code - '0'.code)
        day = (ident[12].code - '0'.code) * 10 + (ident[13].code - '0'.code)
    }
    if (month == 2) {
        if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) {
            // 闰年2月29日
            if (day > 29) {
                return null
            }
        } else {
            if (day > 28) {
                return null
            }
        }
    } else if (month == 4 || month == 6 || month == 9 || month == 11) {
        if (day > 30) {
            return null
        }
    } else if (month <= 12) {
        if (day > 31) {
            return null
        }
    } else {
        return null
    }
    return LocalDate(year,month,day)
}

val StringValidator.Companion.ChineseId get() = stringValidator("chinese id check error %s") { identify(it).first }